package com.weixin.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.servlet.http.HttpServletRequest;

import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.weixin.config.WxConfig;
import com.weixin.config.WxpayConfig;

/**
 * 类描述：微信http工具类<br>
 * 创建人：邓强 <br>
 * 创建时间：2017年10月27日 下午2:11:57 <br>
 * 修改人： <br>
 * 修改时间：2017年10月27日 下午2:11:57 <br>
 * 修改备注： <br>
 * 
 * @version V1.0
 */
public class WxHttp {
	private static Logger log = LoggerFactory.getLogger(WxHttp.class);

	/**
	 * IP校验正则串
	 */
	public static final String IP_REGEX = "^((([1-9]|[1-9][0-9]|1\\d\\d|2[0-4]\\d|25[0-5])))\\.((([0-9]|[1-9][0-9]|1\\d\\d|2[0-4]\\d|25[0-5])))\\.((([0-9]|[1-9][0-9]|1\\d\\d|2[0-4]\\d|25[0-5]))|\\*)\\.((([0-9]|[1-9][0-9]|1\\d\\d|2[0-4]\\d|25[0-5]))|\\*)$";

	/**
	 * 获取请求ip(注意不能异步使用)<br>
	 * <br>
	 * 创建人：邓强 <br>
	 * 创建时间：2017年10月30日 上午10:38:59 <br>
	 * 修改人： <br>
	 * 修改时间：2017年10月30日 上午10:38:59 <br>
	 * 修改备注： <br>
	 * 
	 * @param request
	 * @return
	 */
	public static String getRemoteIp(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");

		if (ip == null || "".equals(ip.trim()) || "unknown".equalsIgnoreCase(ip.trim())) {
			ip = request.getHeader("Proxy-Client-IP");
		} else {
			ip = ip.trim().replaceAll("\\s", "");

			String[] ipList = ip.split(",");

			for (int i = 0; i < ipList.length; i++) {
				if (!ipList[i].equalsIgnoreCase("unknown") && ipList[i].matches(IP_REGEX)) {
					ip = ipList[i];
					break;
				}
			}
		}

		if (ip == null || "".equals(ip.trim()) || "unknown".equalsIgnoreCase(ip.trim())) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}

		if (ip == null || "".equals(ip.trim()) || "unknown".equalsIgnoreCase(ip.trim())) {
			ip = request.getRemoteAddr();
		}

		return ip;
	}

	/**
	 * 判断是否get请求<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:39:37    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:39:37   <br>  
	 * 修改备注：     <br> 
	 * @param request
	 * @return
	 */
	public static boolean isGet(HttpServletRequest request) {
		return "GET".equalsIgnoreCase(request.getMethod());
	}

	/**
	 * 判断是否微信浏览器<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:39:50    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:39:50   <br>  
	 * 修改备注：     <br> 
	 * @param request
	 * @return
	 */
	public static boolean isWxBrowser(HttpServletRequest request) {
		String userAgent = request.getHeader("user-agent");
		if (userAgent != null && userAgent.toLowerCase().indexOf("micromessenger") > 0) {
			return true;
		}
		return false;
	}
	
	/**
	 * 是否ajax请求<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:40:02    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:40:02   <br>  
	 * 修改备注：     <br> 
	 * @param request
	 * @return
	 */
	public static boolean isAjax(HttpServletRequest request) {
		return request.getHeader("x-requested-with") != null
				&& request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest");
	}

	/**
	 * 编码字符串<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:40:13    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:40:13   <br>  
	 * 修改备注：     <br> 
	 * @param str
	 * @return
	 */
	public static String enStr(String str) {
		try {
			if (str == null || "".equals(str)) {
				return "";
			}

			return URLEncoder.encode(str, WxConfig.charset);
		} catch (UnsupportedEncodingException e) {
			log.error("", e);
		}

		return null;
	}

	/**
	 * 编码参数<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:40:28    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:40:28   <br>  
	 * 修改备注：     <br> 
	 * @param paramMap
	 * @return
	 */
	public static String enParam(Map<?, ?> paramMap) {
		try {
			if (paramMap == null) {
				return "";
			}

			String res = "";

			for (Object obj : paramMap.entrySet()) {
				Entry<?, ?> entry = (Entry<?, ?>) obj;

				if (entry.getKey() == null || entry.getValue() == null) {
					continue;
				}

				res = res + "&" + URLEncoder.encode(entry.getKey().toString(), WxConfig.charset);
				res = res + "=" + URLEncoder.encode(entry.getValue().toString(), WxConfig.charset);
			}

			if (!"".equals(res)) {
				res = res.substring(1);
			}

			return res;
		} catch (UnsupportedEncodingException e) {
			log.error("", e);
		}

		return null;
	}
	
	/**
	 * 获取字节<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:40:40    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:40:40   <br>  
	 * 修改备注：     <br> 
	 * @param str
	 * @return
	 */
	public static byte[] getByte(String str) {
		if (str == null || "".equals(str)) {
			return null;
		}

		try {
			return str.getBytes(WxConfig.charset);
		} catch (UnsupportedEncodingException e) {
			log.error("", e);
		}

		return null;
	}

	/**
	 * 输入流转换成xml<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:40:49    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:40:49   <br>  
	 * 修改备注：     <br> 
	 * @param is
	 * @return
	 */
	public static String readXml(InputStream is) {
		try {
			return WxXml.toXml(is, WxConfig.charset);
		} catch (DocumentException e) {
			log.error("", e);
		}

		return null;
	}

	/**
	 * 获取文件的ContentType(jdk1.7)<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:42:49    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:42:49   <br>  
	 * 修改备注：     <br> 
	 * @param file
	 * @return
	 */
	public static String getContentType(File file) {
		try {

			Path path = Paths.get(file.getPath());
			return Files.probeContentType(path);
		} catch (IOException e) {
			log.error("", e);
		}

		return null;
	}

	/**
	 * https请求<br>
	 * <br>
	 * 创建人：邓强 <br>
	 * 创建时间：2017年10月30日 上午10:41:04 <br>
	 * 修改人： <br>
	 * 修改时间：2017年10月30日 上午10:41:04 <br>
	 * 修改备注： <br>
	 * 
	 * @param requestUrl
	 * @param body
	 * @return
	 * @throws IOException
	 * @throws KeyManagementException
	 * @throws NoSuchAlgorithmException
	 */
	public static String httpsRequest(String requestUrl, String body)
			throws IOException, KeyManagementException, NoSuchAlgorithmException {
		
		SSLSocketFactory ssl = MySSLSocketFactory.getSSLSocketFactory("TLS");
		HostnameVerifier hostVer = new HostnameVerifier() { // 主机名(域名校验)
			@Override
			public boolean verify(String hostname, SSLSession session) {
				return true; // 信任所有主机
			}
		};
		
		return httpsRequest(requestUrl, ssl, hostVer, body);
	}

	/**
	 * 微信提现请求<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:43:30    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:43:30   <br>  
	 * 修改备注：     <br> 
	 * @param wxpayConfig
	 * @param requestUrl
	 * @param body
	 * @return
	 * @throws Exception
	 */
	public static String withdrawals(WxpayConfig wxpayConfig, String requestUrl, String body) throws Exception {
		
		SSLSocketFactory ssl = MySSLSocketFactory.getSSLSocketFactory(wxpayConfig.getAgreement(),
				wxpayConfig.getKeyType(), wxpayConfig.getCertPath(), wxpayConfig.getMchId(),
				wxpayConfig.getKeyManager());
		
		HostnameVerifier hostVer = new HostnameVerifier() { // 主机名(域名校验)
			@Override
			public boolean verify(String hostname, SSLSession session) {
				return true; // 信任所有主机
			}
		};
		
		return httpsRequest(requestUrl, ssl, hostVer, body);
	}
	
	/**
	 * https302请求<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:42:59    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:42:59   <br>  
	 * 修改备注：     <br> 
	 * @param requestUrl
	 * @return
	 * @throws IOException
	 * @throws KeyManagementException
	 * @throws NoSuchAlgorithmException
	 */
	public static String https302(String requestUrl)
			throws IOException, KeyManagementException, NoSuchAlgorithmException {
		HttpsURLConnection httpUrlConn = null;
		String resStr = null;

		try {
			String methodType = "GET";

			SSLSocketFactory ssl = MySSLSocketFactory.getSSLSocketFactory("TLS");
			HostnameVerifier hostVer = new HostnameVerifier() { // 主机名(域名校验)
				@Override
				public boolean verify(String hostname, SSLSession session) {
					return true; // 信任所有主机
				}
			};

			URL url = new URL(requestUrl);
			httpUrlConn = (HttpsURLConnection) url.openConnection();
			httpUrlConn.setSSLSocketFactory(ssl);
			httpUrlConn.setHostnameVerifier(hostVer);
			httpUrlConn.setDoOutput(true);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			httpUrlConn.setRequestMethod(methodType);
			httpUrlConn.setConnectTimeout(60000);
			httpUrlConn.setReadTimeout(60000);
			httpUrlConn.addRequestProperty("user-agent",
					"Mozilla/5.0 (Linux; U; Android 5.0.1; zh-cn; M353 Build/LRX22C) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/5.4 TBS/025488 Mobile Safari/533.1 MicroMessenger/6.3.9.48_refecd3e.700 NetType/WIFI Language/zh_CN");
			httpUrlConn.setInstanceFollowRedirects(false);
			// HttpsURLConnection.setFollowRedirects(false);

			httpUrlConn.connect();

			httpUrlConn.getInputStream();

			int status = httpUrlConn.getResponseCode();
			if (status != HttpURLConnection.HTTP_OK) {
				if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM
						|| status == HttpURLConnection.HTTP_SEE_OTHER)
					resStr = httpUrlConn.getHeaderField("Location");
			}
		} finally {
			if (httpUrlConn != null) {
				httpUrlConn.disconnect();
			}
		}

		return resStr;
	}
	
	/**
	 * https请求<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:41:04    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:41:04   <br>  
	 * 修改备注：     <br> 
	 * @param requestUrl
	 * @param body
	 * @return
	 * @throws IOException
	 * @throws KeyManagementException
	 * @throws NoSuchAlgorithmException
	 */
	public static String httpsRequest(String requestUrl, SSLSocketFactory ssl, HostnameVerifier hostVer, String body)
			throws IOException, KeyManagementException, NoSuchAlgorithmException {
		HttpsURLConnection httpUrlConn = null;
		OutputStream os = null;
		BufferedReader br = null;
		String resStr = null;

		try {
			String methodType = "GET";
			if (body != null) {
				methodType = "POST";
			}

			URL url = new URL(requestUrl);
			httpUrlConn = (HttpsURLConnection) url.openConnection();
			httpUrlConn.setSSLSocketFactory(ssl);
			httpUrlConn.setHostnameVerifier(hostVer);
			httpUrlConn.setDoOutput(true);
			httpUrlConn.setDoInput(true);
			httpUrlConn.setUseCaches(false);
			httpUrlConn.setRequestMethod(methodType);
			httpUrlConn.setConnectTimeout(60000);
			httpUrlConn.setReadTimeout(60000);

			httpUrlConn.connect();

			if ("POST".equals(methodType)) {
				if (!"".equals(body)) {
					os = httpUrlConn.getOutputStream();
					os.write(body.getBytes(WxConfig.charset));
					os.flush();
				}
			}

			br = new BufferedReader(new InputStreamReader(httpUrlConn.getInputStream(), WxConfig.charset));
			String line = null;
			StringBuilder sb = new StringBuilder();
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}

			resStr = sb.toString();
		} finally {
			try {
				if (br != null) {
					br.close();
				}
				if (os != null) {
					os.close();
				}
				if (httpUrlConn != null) {
					httpUrlConn.disconnect();
				}
			} catch (IOException e) {
				log.error("", e);
			}
		}

		return resStr;
	}
	
	/**
	 * 文件上传<br>
	 * <br>  
	 * 创建人：邓强   <br>
	 * 创建时间：2017年10月30日 上午10:42:24    <br> 
	 * 修改人：  <br>
	 * 修改时间：2017年10月30日 上午10:42:24   <br>  
	 * 修改备注：     <br> 
	 * @param uploadUrl
	 * @param file
	 * @param inputName
	 * @return
	 * @throws IOException
	 */
	public static String uploadFile(String uploadUrl, File file, String inputName) throws IOException {
		String end = "\r\n";
		String twoHyphens = "--";
		String boundary = "*********";

		InputStream in = null; // 文件输入流
		OutputStream os = null; // http输出流
		InputStream is = null; // http输入流
		InputStreamReader isr = null; // http输入流
		BufferedReader br = null; // http输入流
		try {
			String fileName = file.getName();
			String contentType = getContentType(file);

			URL url = new URL(uploadUrl);
			HttpURLConnection con = (HttpURLConnection) url.openConnection();
			con.setDoInput(true);
			con.setDoOutput(true);
			con.setUseCaches(false);
			con.setRequestMethod("POST");
			con.setRequestProperty("Connection", "Keep-Alive");
			con.setRequestProperty("Charset", WxConfig.charset);
			con.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

			os = con.getOutputStream();
			os.write(getByte(twoHyphens + boundary + end));
			os.write(getByte(
					"Content-Disposition: form-data; name=\"" + inputName + "\";filename=\"" + fileName + "\"" + end));
			os.write(getByte("Content-Type: " + contentType + end));
			os.write(getByte(end));

			in = new FileInputStream(file);
			byte[] buf = new byte[1024];
			int len = -1;
			while ((len = in.read(buf)) != -1) {
				os.write(buf, 0, len);
			}
			os.write(getByte(end));
			os.write(getByte(twoHyphens + boundary + twoHyphens + end));
			os.flush();

			is = con.getInputStream();
			isr = new InputStreamReader(is, WxConfig.charset);
			br = new BufferedReader(isr);
			StringBuilder sb = new StringBuilder();
			String line = null;
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}

			return sb.toString();
		} finally {
			try {
				if (in != null) {
					in.close();
				}
				if (os != null) {
					os.close();
				}
				if (br != null) {
					br.close();
				}
				if (isr != null) {
					isr.close();
				}
				if (is != null) {
					is.close();
				}
			} catch (IOException e) {
				log.error("", e);
			}
		}
	}
}
